package cn.lingyangwl.agile.msg.push.service.impl;

import cn.lingyangwl.agile.common.core.exception.enums.*;
import cn.lingyangwl.agile.common.security.utils.*;
import cn.lingyangwl.agile.dynamic.config.core.*;
import cn.lingyangwl.agile.infra.*;
import cn.lingyangwl.agile.infra.common.*;
import cn.lingyangwl.agile.model.module.auth.*;
import cn.lingyangwl.agile.msg.push.api.*;
import cn.lingyangwl.agile.msg.push.api.constatnts.*;
import cn.lingyangwl.agile.msg.push.api.model.rqrs.*;
import cn.lingyangwl.agile.msg.push.assembly.*;
import cn.lingyangwl.agile.msg.push.manager.*;
import cn.lingyangwl.agile.msg.push.mapper.*;
import cn.lingyangwl.agile.msg.push.model.entity.*;
import cn.lingyangwl.agile.msg.push.model.rqrs.notice.*;
import cn.lingyangwl.agile.msg.push.model.rqrs.receiver.*;
import cn.lingyangwl.agile.msg.push.service.*;
import cn.lingyangwl.framework.data.mate.annotations.*;
import cn.lingyangwl.framework.data.mate.fieldsensitive.*;
import cn.lingyangwl.framework.mybatis.plus.utils.*;
import cn.lingyangwl.framework.tool.core.*;
import cn.lingyangwl.framework.tool.core.exception.*;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.IService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * 消息接收者
 *
 * @author shenguangyang
 */
@Slf4j
@Service
public class ReceiverServiceImpl extends ServiceImpl<ReceiverMapper, Receiver>
    implements IService<Receiver>, ReceiverService {
    @Resource
    private InfraApi infraApi;
    @Resource
    private ReceiverAssembly msgReceiverAssembly;
    @Resource
    private MsgPushApi msgPushApi;
    @Resource
    private ConfigFactory configFactory;
    @Resource
    private FieldSensitiveHandler fieldSensitiveHandler;
    @Resource
    private ReceiverSubscribeManager receiverSubscribeManager;
    @Resource
    private MsgNoticeManager msgNoticeManager;
    @Resource
    private NoticeChannelService noticeChannelService;
    @Resource
    private MsgNoticeAssembly msgNoticeAssembly;
    
    @Override
    @FieldSensitiveHandle
    public IPage<ReceiverResp> pageReceiver(ReceiverPageReq req) {
        LocalDateTime beginTime = req.getBeginLocalDateTime();
        LocalDateTime endTime = req.getEndLocalDateTime();
        LambdaQueryWrapper<Receiver> lqw = Receiver.lqw()
            .like(StringUtils.isNotEmpty(req.getKeyWord()), Receiver::getPhone, req.getKeyWord())
            .like(StringUtils.isNotEmpty(req.getKeyWord()), Receiver::getEmail, req.getKeyWord())
            .ge(Objects.nonNull(beginTime), Receiver::getCreatedAt, beginTime)
            .le(Objects.nonNull(endTime), Receiver::getCreatedAt, endTime)
            .orderByDesc(Receiver::getCreatedAt);
        IPage<Receiver> pageData = this.page(PageUtil.to(req), lqw);
        return PageUtil.to(pageData, msgReceiverAssembly::toReceiverResp);
    }

    @Override
    @FieldSensitiveHandle
    public ReceiverResp getReceiver(Long id) {
        Receiver entity = this.getById(id);
        ReceiverResp resp = msgReceiverAssembly.toReceiverResp(entity);

        LoginUser loginUser = SecurityUtils.getLoginUser();
        JSONObject params = new JSONObject();
        params.put(ScanScene.USER_ID, loginUser.getUserId());
        params.put(ScanScene.LOGIN_ID, loginUser.getLoginId());

        // 获取微信公众号二维码, 扫码获取openId
        // 当用户扫码之后, 会在 infra 服务中收到微信服务器的推送扫码事件, 然后由infra服务将数据推送到push服务最终通过websocket返回给前端
        ScanScene scanScene = ScanScene.builder().isRefresh(false).sceneCode(QrCodeSceneEnum.GET_OPENID.getCode())
                .params(params).build();
        String url = infraApi.getMpQrcode(scanScene);
        resp.setWxMpQrCodeUrl(url);
        return resp;
    }

    @Override
    public boolean saveReceiver(ReceiverSaveReq req) {
        Receiver entity = msgReceiverAssembly.toReceiver(req);

        LambdaQueryWrapper<Receiver> lqw = Receiver.lqw()
                .or(e -> e.eq(Receiver::getEmail, req.getEmail()).isNotNull(Receiver::getEmail))
                .or(e -> e.eq(Receiver::getPhone, req.getPhone()).isNotNull(Receiver::getPhone));
        long count = this.count(lqw);
        if (count > 0) {
            throw new BizException("手机号或者邮箱已存在");
        }
        return this.save(entity);
    }

    @Override
    @FieldSensitiveHandle
    public List<ReceiverResp> listMsgSubscriber(Long noticeId) {
        List<Receiver> receiverList = this.receiverSubscribeManager.listMsgSubscriber(noticeId);
        return receiverList.stream().map(msgReceiverAssembly::toReceiverResp).collect(Collectors.toList());
    }

    @Override
    @FieldBindHandle
    public List<SubscribeMsgNoticeResp> listSubscribeMsg(SubscribeMsgListReq req) {
        Long receiverId = req.getReceiverId();
        LambdaQueryWrapper<ReceiverSubscribe> lqw = ReceiverSubscribe.lqw().eq(ReceiverSubscribe::getReceiverId, receiverId);
        List<Long> noticeIds = receiverSubscribeManager.list(lqw).stream().map(ReceiverSubscribe::getNoticeId).distinct()
                .collect(Collectors.toList());
        if (CollectionUtils.isEmpty(noticeIds)) {
            return Collections.emptyList();
        }

        LambdaQueryWrapper<MsgNotice> noticeLqw = MsgNotice.lqw().in(MsgNotice::getId, noticeIds)
                .and(StringUtils.isNotEmpty(req.getKeyWord()), e -> {
                     e.like(StringUtils.isNotEmpty(req.getKeyWord()), MsgNotice::getCode, req.getKeyWord());
                     e.or(e1 -> e1.like(StringUtils.isNotEmpty(req.getKeyWord()), MsgNotice::getName, req.getKeyWord()));
                });

        List<MsgNotice> noticeList = msgNoticeManager.list(noticeLqw);

        return noticeList.stream().map(msgNoticeAssembly::toSubscribeMsgNoticeResp).collect(Collectors.toList());
    }

    @Override
    public void saveMsgSubscriber(MsgSubscriberChangeReq req) {
        LambdaQueryWrapper<ReceiverSubscribe> lqw = ReceiverSubscribe.lqw().in(ReceiverSubscribe::getNoticeId, req.getNoticeIds());
        List<ReceiverSubscribe> subscribeList = this.receiverSubscribeManager.list(lqw);

        List<Long> existReceiverIds = subscribeList.stream().map(ReceiverSubscribe::getId).collect(Collectors.toList());

        List<ReceiverSubscribe> list = req.getReceiverIds().stream().filter(e -> !existReceiverIds.contains(e)).flatMap(receiverId -> {
            List<Long> noticeIds = req.getNoticeIds();
            return noticeIds.stream().map(noticeId -> {
                ReceiverSubscribe entity = new ReceiverSubscribe();
                entity.setReceiverId(receiverId);
                entity.setNoticeId(noticeId);
                return entity;
            });
        }).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(list)) {
            return;
        }
        this.receiverSubscribeManager.saveBatch(list);
    }

    @Override
    public void deleteMsgSubscriber(MsgSubscriberChangeReq req) {
        if (req.getNoticeIds().size() > 1) {
            throw new BizException("所选消息通知数不能 > 1");
        }
        LambdaQueryWrapper<ReceiverSubscribe> lqw = ReceiverSubscribe.lqw()
                .in(ReceiverSubscribe::getReceiverId, req.getReceiverIds())
                .eq(ReceiverSubscribe::getNoticeId, req.getNoticeIds().get(0));
        this.receiverSubscribeManager.remove(lqw);
    }

    @Override
    public boolean updateReceiver(ReceiverUpdateReq req) {
        Receiver entity = msgReceiverAssembly.toReceiver(req);

        // 校验是否重复
        LambdaQueryWrapper<Receiver> lqw = Receiver.lqw()
                .ne(Receiver::getId, req.getId())
                .and(e -> e.or(e1 -> e1.eq(Receiver::getEmail, req.getEmail()).isNotNull(Receiver::getEmail))
                        .or(e1 -> e1.eq(Receiver::getPhone, req.getPhone()).isNotNull(Receiver::getPhone)));

        long count = this.count(lqw);
        if (count > 0) {
            throw new BizException("手机号或者邮箱已存在");
        }

        // 处理经过脱敏处理的字段
        // 1. 先解析数据库中存储的字段, 并进行脱敏处理
        Receiver dbEntity = this.getById(req.getId());
        if (Objects.isNull(dbEntity)) {
            throw new BizException(GlobalErrorEnum.OPERATION_FAIL_SELETE);
        }
        // 数据库存储的字段值
        JSONObject dbEntityJson = JSONObject.from(dbEntity);

        // 2. 对数据库中字段进行脱敏处理
        ReceiverResp dbReceiverResp = msgReceiverAssembly.toReceiverResp(dbEntity);
        fieldSensitiveHandler.handleField(dbReceiverResp);
        JSONObject oldSensitiveEntity = JSONObject.from(dbReceiverResp);
        JSONObject newEntity = JSONObject.from(entity);

        // 3. 将前两步骤中的值进行比对, 如果一样就将数据库中的值赋值到req的某个字段
        newEntity.forEach((k, v) -> {
            if (Objects.equals(String.valueOf(oldSensitiveEntity.get(k)), String.valueOf(v))) {
                newEntity.put(k, dbEntityJson.get(k));
            }
        });
    	return this.updateById(JSON.to(Receiver.class, newEntity));
    }

    @Override
    public boolean deleteReceiver(Long id) {
    	return this.removeById(id);
    }
    
    @Override
    public boolean batchDeleteReceiver(List<Long> ids) {
    	return this.removeByIds(ids);
    }

    @Override
    public void checkCaptchaFromAdmin(ReceiverVerifyReq req) {
        String toUser = checkAndGetReceiver(req.getTaskCode(), req.getReceiverId());

        // 检验邮箱/手机号验证
        CheckNoAuthUserCaptchaReq checkReq = new CheckNoAuthUserCaptchaReq();
        checkReq.setTaskCode(req.getTaskCode());
        checkReq.setVerifyCode(req.getVerifyCode());
        checkReq.setToUser(toUser);
        msgPushApi.checkNoAuthUserCaptcha(checkReq);

        // 校验成功验证状态
        Receiver entity = new Receiver();
        entity.setId(req.getReceiverId());
        if (Objects.equals(req.getTaskCode(), NoticeCodeEnum.VERIFY_EMAIL.getCode())) {
            entity.setEmailStatus(Receiver.Status.VERIFY);
            this.updateById(entity);
        } else if (Objects.equals(req.getTaskCode(), NoticeCodeEnum.VERIFY_SMS.getCode())) {
            entity.setPhoneStatus(Receiver.Status.VERIFY);
            this.updateById(entity);
        }
    }

    @Override
    public void sendCaptcha(SendCaptchaToReceiverReq req) {
        String toUser = checkAndGetReceiver(req.getTaskCode(), req.getReceiverId());
        req.setToUser(toUser);

        // 这里调用 发送未授权用户验证码消息方法的原因是可以自行指定接收者, 如果调用发送给已授权的用户消息, 则不能指定接收者
        msgPushApi.sendNoAuthUserCaptcha(req);
    }

    public String checkAndGetReceiver(String taskCode, Long receiverId) {
        Receiver receiver = this.getById(receiverId);
        if (Objects.isNull(receiver)) {
            throw new BizException(GlobalErrorEnum.OPERATION_FAIL_SELETE);
        }
        if (Objects.equals(taskCode, NoticeCodeEnum.VERIFY_EMAIL.getCode())) {
            return receiver.getEmail();
        } else if (Objects.equals(taskCode, NoticeCodeEnum.VERIFY_SMS.getCode())) {
            return receiver.getPhone();
        } else {
            throw new BizException("目前只支持手机号和邮箱的发送");
        }
    }

}
